#!/usr/bin/python3
# -*- coding: utf-8 -*
"""
Description: Log parser.
Class: LogParser
"""
import re
from datetime import datetime
from pygrok import Grok

from ..libs.log import LOGGER
from . import Parser


class LogParser(Parser):
    """
    class LogParser is the parser that parses the log to the formmated data.
    """

    def parse_content(self, key, content):
        """
        Parse the log data to formmated data, add ip and time information.

        Args:
            key (str): the main key of the content.
            content (str): raw data.

        Returns:
            dict: parsed content.
            bool: parse succeed or fail.
        """
        content = self.parse_to_block(key, content)
        res = []
        matched_grok = None

        if len(content) == 0:
            LOGGER.warning("'%s' has no content.", key)
            return content, True

        if 'matched_grok' not in self.param_config.keys():
            self.get_pattern_information(key, content[0])

        if self.param_config['matched_grok'] is None:
            LOGGER.error(
                "No matched pattern for '%s', return raw content.", key)
            res = list(map(lambda value: {"raw_message": value}, content))
            return res, False

        matched_grok = self.param_config.get("matched_grok")
        time_position = self.param_config.get('time_position')
        pattern_key = self.param_config.get('pattern_key')
        time_key = self.param_config.get('time_key')
        pattern_len = len(pattern_key) if pattern_key is not None else 0

        for log in content:
            match_obj = matched_grok.regex_obj.search(log)

            if match_obj is None:
                continue

            log_formated = match_obj.groupdict()
            # get all matched content for each name.
            # ('Sun Dec 04 04:47:44 2005', 'Sun', 'Dec', '04', '47', '44', '2005', 'notice',
            # 'workenv.init()', 'message')
            # use patten without name
            if pattern_key is not None:
                index = 0
                while index < time_position:
                    log_formated[pattern_key[index][1]] = match_obj.groups()[
                        index]
                    index += 1
                index = 0
                while index < pattern_len - time_position - 1:
                    log_formated[pattern_key[-1 - index]
                                 [1]] = match_obj.groups()[-1 - index]
                    index += 1
            # get time succeed
            if time_key is not None:
                try:
                    log_formated['timestamp'] = self.get_time(
                        len(time_key), time_key, time_position, match_obj.groups())
                except TypeError:
                    LOGGER.error("Timestamp parsed failed for '%s'.", key)

            res.append(log_formated)

        return res, True

    @staticmethod
    def get_time(time_key_len, time_key, time_position, groups):
        """
        Get datetime from the tiem string.

        Args:
            time_key_len (int): length of the time key.
            time_key (list): stores each element of the time.
            time_position (int): the position of the timestamp in the original pattern.
            groups (list): list of time element parsed by regex.

        Returns:
            dict: updated configuration, None means exception raised.
        """
        time_map = {}
        months = ['-', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
                  'Nov', 'Dec', '-', 'January', 'February', 'March', 'April', 'May', 'June',
                  'July', 'August', 'September', 'Octorber', 'November', 'December']

        for k in range(time_key_len):
            time_map[time_key[k]] = groups[k + time_position + 1]

        if 'year' not in time_map.keys():
            year = datetime.now().year
        else:
            year = int(time_map.get('year'))

        if 'second' in time_map.keys():
            time_map['second'] = re.split(r'[,.]', time_map['second'])[0]

        day, hour, minute, second = map(int, [time_map.get('monthday'), time_map.get(
            'hour'), time_map.get('minute'), time_map.get('second')])

        try:
            month = int(time_map.get('monthnum'))
        except TypeError:
            month = months.index(time_map.get('month')) % 13

        time = datetime(year, month, day, hour, minute, second)

        return time

    def get_pattern_information(self, key, head):
        """
        Get matched pattern and extract the time pattern.

        Args:
            key (str)
            head (str): first line of the content.

        Returns:
            dict: updated configuration, None means exception raised.
        """
        # try to get matched pattern in beat configuration.
        matched_grok, matched_pattern = self.get_matched_pattern(key, head)
        # don't get matched pattern, then turn to find in regex configuration.
        if matched_grok is None or matched_pattern == "":
            matched_grok, matched_pattern = self.find_matched_pattern_by_type(
                head, 'log')
            if matched_grok is None:
                self.param_config['matched_grok'] = None
                return

        self.param_config['matched_grok'] = matched_grok
        pattern_key = re.findall(
            r'%\{(\w+):?(\w+)?(?::\w+)?\}', matched_pattern)
        time_position = None
        use_pattern_without_name = True

        for i, _ in enumerate(pattern_key):
            name = pattern_key[i][1].strip(':')
            if name == 'timestamp':
                time_position = i
            if name == '':
                use_pattern_without_name = False

        if time_position is None:
            LOGGER.warning("'timestamp' is not in '%s' for '%s'.",
                           matched_pattern, key)
            return

        # APACHETIMESTAMP
        time_regex = matched_grok.predefined_patterns[pattern_key[time_position][0]].regex_str
        time_name_raw = re.findall(r'\{(\w+)', time_regex)
        self.param_config['time_key'] = list(
            map(lambda key: key.lower(), time_name_raw))
        self.param_config['time_position'] = time_position

        if use_pattern_without_name:
            self.param_config['pattern_key'] = pattern_key
            self.param_config['matched_grok'] = Grok(re.sub(r'(:\w+)', '', matched_pattern),
                                                     self.regex_config.get('custom_file'))

        return


def get_class():
    """
    get LogParser class.

    Args:

    Returns:
        class
    """
    return LogParser
